package aggregate

import (
	"context"
	"log"
	"strings"
	"sync"
	"time"

	"github.com/robfig/cron/v3"
)

// TimeoutAlertScheduler 超时提醒调度器
// 负责定时检查工作流节点是否超时，并发送超时提醒事件
type TimeoutAlertScheduler struct {
	cron         *cron.Cron
	reference    Reference
	eventService EventService
	ctx          context.Context
	cancel       context.CancelFunc
	wg           sync.WaitGroup
	running      bool
	mu           sync.RWMutex
}

// NewTimeoutAlertScheduler 创建超时提醒调度器
// 参数:
//   - reference: 数据访问引用
//   - eventService: 事件服务
//
// 返回值:
//   - *TimeoutAlertScheduler: 调度器实例
func NewTimeoutAlertScheduler(reference Reference, eventService EventService) *TimeoutAlertScheduler {
	ctx, cancel := context.WithCancel(context.Background())
	scheduler := &TimeoutAlertScheduler{
		cron:         cron.New(),
		reference:    reference,
		eventService: eventService,
		ctx:          ctx,
		cancel:       cancel,
		running:      false,
	}

	return scheduler
}

// Start 启动定时任务
// 功能: 添加定时任务到cron调度器并启动
func (s *TimeoutAlertScheduler) Start() {
	s.mu.Lock()
	defer s.mu.Unlock()

	if s.running {
		log.Println("Timeout alert scheduler is already running")
		return
	}

	// 每分钟执行一次超时检查
	_, err := s.cron.AddFunc("@every 1m", s.processTimeoutNodesWrapper)
	if err != nil {
		log.Printf("Failed to add timeout alert cron job: %v", err)
		return
	}

	s.cron.Start()
	s.running = true
	log.Println("Timeout alert scheduler started")
}

// Stop 停止定时任务
// 功能: 优雅停止cron调度器，等待正在执行的任务完成
func (s *TimeoutAlertScheduler) Stop() {
	s.mu.Lock()
	defer s.mu.Unlock()

	if !s.running {
		log.Println("Timeout alert scheduler is not running")
		return
	}

	log.Println("Stopping timeout alert scheduler...")

	// 停止cron调度器，不再接受新的任务
	if s.cron != nil {
		s.cron.Stop()
	}

	// 取消context，通知正在执行的任务停止
	s.cancel()

	// 等待所有正在执行的任务完成，最多等待5秒
	done := make(chan struct{})
	go func() {
		s.wg.Wait()
		close(done)
	}()

	select {
	case <-done:
		log.Println("All timeout alert tasks completed")
	case <-time.After(5 * time.Second):
		log.Println("Timeout waiting for timeout alert tasks to complete, forcing shutdown")
	}

	s.running = false
	log.Println("Timeout alert scheduler stopped")
}

// IsRunning 检查调度器是否正在运行
// 返回值:
//   - bool: 调度器运行状态
func (s *TimeoutAlertScheduler) IsRunning() bool {
	s.mu.RLock()
	defer s.mu.RUnlock()
	return s.running
}

// processTimeoutNodesWrapper 处理超时节点的包装器
// 功能: 管理goroutine生命周期，确保优雅关闭
func (s *TimeoutAlertScheduler) processTimeoutNodesWrapper() {
	s.wg.Add(1)
	defer s.wg.Done()

	// 检查是否已经被取消
	select {
	case <-s.ctx.Done():
		return
	default:
	}

	s.processTimeoutNodes()
}

// processTimeoutNodes 处理超时节点
// 功能: 查询所有超时的节点及其审批任务，并为每个节点发送超时提醒事件
// 优化: 使用一次查询获取节点和任务信息，避免N+1查询问题
func (s *TimeoutAlertScheduler) processTimeoutNodes() {
	// 1. 一次性查询所有超时节点及其审批任务信息
	// 2. 按节点ID分组处理审批任务
	// 3. 为每个节点发送超时提醒事件

	// 使用可取消的context，支持优雅关闭
	ctx, cancel := context.WithTimeout(s.ctx, 5*time.Minute)
	defer cancel()
	timeoutNodesWithTasks, err := s.reference.QueryTimeoutNodesWithTasks(ctx)
	if err != nil {
		log.Printf("Failed to query timeout nodes with tasks: %v", err)
		return
	}

	// 按节点ID分组处理
	nodeTasksMap := s.groupTasksByNodeID(timeoutNodesWithTasks)

	// 为每个节点发送超时提醒事件
	for nodeID, nodeWithTasks := range nodeTasksMap {
		s.processNodeTimeoutAlert(ctx, nodeID, nodeWithTasks)
	}
}

// groupTasksByNodeID 按节点ID分组超时节点和任务信息
// 参数:
//   - timeoutNodesWithTasks: 超时节点及任务信息列表
//
// 返回值:
//   - map[string][]TimeoutNodeWithTasks: 按节点ID分组的任务信息
//
// 功能: 将查询结果按节点ID分组，便于后续处理
func (s *TimeoutAlertScheduler) groupTasksByNodeID(timeoutNodesWithTasks []TimeoutNodeWithTasks) map[string][]TimeoutNodeWithTasks {
	// 1. 创建分组映射
	// 2. 遍历所有记录，按节点ID分组

	nodeTasksMap := make(map[string][]TimeoutNodeWithTasks)
	for _, nodeWithTask := range timeoutNodesWithTasks {
		nodeTasksMap[nodeWithTask.NodeID] = append(nodeTasksMap[nodeWithTask.NodeID], nodeWithTask)
	}
	return nodeTasksMap
}

// processNodeTimeoutAlert 处理单个节点的超时提醒
// 参数:
//   - ctx: 上下文
//   - nodeID: 节点ID
//   - nodeWithTasks: 该节点的所有任务信息
//
// 功能: 聚合审核人ID，发送超时提醒事件
func (s *TimeoutAlertScheduler) processNodeTimeoutAlert(ctx context.Context, nodeID string, nodeWithTasks []TimeoutNodeWithTasks) {
	// 1. 从任务列表中提取节点基本信息
	// 2. 聚合审核人ID并去重
	// 3. 发送超时提醒事件

	// 检查是否已经被取消
	select {
	case <-ctx.Done():
		return
	default:
	}

	if len(nodeWithTasks) == 0 {
		return
	}

	// 获取节点基本信息（所有记录的节点信息相同，取第一个即可）
	firstRecord := nodeWithTasks[0]

	// 聚合审核人ID
	var reviewerIDs []string
	for _, nodeWithTask := range nodeWithTasks {
		// 只处理审核中状态的任务（状态为1）
		if nodeWithTask.TaskStatus == 1 {
			reviewerIDs = append(reviewerIDs, nodeWithTask.ReviewerID)
		}
	}

	// 去重审核人ID
	reviewerIDs = s.removeDuplicateStrings(reviewerIDs)

	// 如果没有待审核的任务，则不发送提醒
	if len(reviewerIDs) == 0 {
		return
	}

	// 再次检查是否已经被取消
	select {
	case <-ctx.Done():
		return
	default:
	}

	// 检查该节点是否已经发送过提醒，防止重复提醒
	alertRecords, err := s.reference.CheckTimeoutAlertRecords(ctx, []string{nodeID}, firstRecord.WorkflowID)
	if err != nil {
		log.Printf("Failed to check timeout alert records for node %s: %v", nodeID, err)
		return
	}

	// 如果该节点已经发送过提醒，则跳过
	if alerted, exists := alertRecords[nodeID]; exists && alerted {
		log.Printf("Node %s has already been alerted, skipping", nodeID)
		return
	}

	// 最后检查是否已经被取消
	select {
	case <-ctx.Done():
		return
	default:
	}

	// 发送超时提醒事件
	event := NewTimeoutAlertEvent(firstRecord.WorkflowID, firstRecord.NodeID, reviewerIDs)
	err = s.eventService.publish(ctx, event)
	if err != nil {
		log.Printf("Failed to publish timeout alert event for node %s: %v", nodeID, err)
		return
	}

	// 记录提醒状态，确保该节点不会被重复提醒
	err = s.reference.CreateTimeoutAlertRecord(ctx, nodeID, firstRecord.WorkflowID)
	if err != nil {
		log.Printf("Failed to create timeout alert record for node %s: %v", nodeID, err)
		// 注意：这里不返回，因为提醒已经发送成功，记录失败不应影响提醒功能
	}
}

// removeDuplicateStrings 去除字符串切片中的重复元素
// 参数:
//   - slice: 原始字符串切片
//
// 返回值:
//   - []string: 去重后的字符串切片
func (s *TimeoutAlertScheduler) removeDuplicateStrings(slice []string) []string {
	// 1. 使用map记录已存在的字符串
	// 2. 遍历原切片，只添加未存在的字符串到结果切片

	seen := make(map[string]bool)
	result := make([]string, 0, len(slice))

	for _, str := range slice {
		str = strings.TrimSpace(str)
		if str != "" && !seen[str] {
			seen[str] = true
			result = append(result, str)
		}
	}

	return result
}
